package asynctask

import (
	"fmt"
	"github.com/pkg/errors"
	"scase.io/application-auto-scaling-service/pkg/cloudresource"
	"scase.io/application-auto-scaling-service/pkg/common"
	"scase.io/application-auto-scaling-service/pkg/db"
	event "scase.io/application-auto-scaling-service/pkg/service/event"
	"scase.io/application-auto-scaling-service/pkg/setting"
	"scase.io/application-auto-scaling-service/pkg/utils/logger"
	"time"
)

type DelPodTask struct {
	podName   string
	projectId string
	groupId   string
	BaseTask
	event *event.EventService
}

func NewDelPodTask(podName string, projectId string, groupId string) *DelPodTask {
	return &DelPodTask{
		podName:   podName,
		projectId: projectId,
		groupId:   groupId,
	}
}

// GetKey get id of the resource corresponding to the task
func (t *DelPodTask) GetKey() string {
	return t.podName
}

// GetType get task type
func (t *DelPodTask) GetType() string {
	return db.TaskTypeDeletePod
}

func (t *DelPodTask) Run(log *logger.FMLogger) error {
	taskRetryTimes := t.GetRetryTimes()
	if taskRetryTimes > common.AsyncMaxRetryTimes {
		return t.handlerTaskComplete(log)
	}
	group, err := db.ScalingGroupTable().GetScalingGroupById(t.groupId)
	if err != nil {
		log.Error("[delete-pod] get scaling group [%s] error:%+v", t.groupId, err)
		return t.handlerTaskComplete(log)
	}
	podCtrl, err := cloudresource.GetPodController(group.ProjectId)
	if err != nil {
		log.Error("[delete-pod] get controller of group [%s] error:%+v", t.groupId, err)
		return err
	}
	eventService, err := event.NewEventService(t.projectId, nil, log)
	if err != nil {
		log.Error("[delete-pod]new event service error:%+v", err)
		return err
	}
	t.event = eventService

	//  clean up 并wait
	if err := t.StartCleanProcess(log, group, podCtrl); err != nil {
		log.Error("[delete-pod] start clean process of namespace %s pod %s error:%+v", group.FleetId, t.podName, err)
		go t.event.BuildAndRegisterEvent("[delete-pod] failed", t.podName, fmt.Sprintf("delete-vm task [%s] failed with error:%+v", t.podName, err.Error()),
			fmt.Sprintf("delete vm [%s] failed", t.podName), common.EventTraceStateIncident)
		return err
	}
	// 删除pod
	if err := podCtrl.DeletePod(group.FleetId, t.podName); err != nil {
		log.Error("[delete-pod] delete namespace %s pod %s error:%+v", group.FleetId, t.podName, err)
		go t.event.BuildAndRegisterEvent("[delete-pod] failed", t.podName, fmt.Sprintf("delete-vm task [%s] failed with error:%+v", t.podName, err.Error()),
			fmt.Sprintf("delete vm [%s] failed", t.podName), common.EventTraceStateIncident)
		return err
	}
	go t.event.BuildAndRegisterEvent("[delete-pod] success", t.podName, fmt.Sprintf(" delete pod [%s] success", t.podName), fmt.Sprintf("delete pod [%s] success", t.podName), common.EventTraceStateNormal)
	return t.handlerTaskComplete(log)
}

func (t *DelPodTask) StartCleanProcess(log *logger.FMLogger, group *db.ScalingGroup, podCtrl *cloudresource.PodResourceController) error {
	podInfo, err := db.PodInfoTable().Get(db.Filters{"HostName": t.podName})
	if err != nil {
		log.Error("[delete-pod] get pod [%s] error:%+v", t.podName, err)
		return err
	}
	// 检查pod是否存在
	_, err = podCtrl.QueryPod(group.FleetId, t.podName)
	if err != nil {
		if errors.Is(err, common.ErrPodNotFound) {
			log.Warn("[delete-pod] namespace %s pod %s not found", group.FleetId, t.podName)
			return t.handlerTaskComplete(log)
		}
		log.Error("[delete-pod] query namespace %s pod %s error:%+v", group.FleetId, t.podName, err)
		return err
	}
	var IP string
	if setting.ConnectToAuxproxyByIP == common.PublicIP {
		IP = podInfo.PublicIp
	} else {
		IP = podInfo.PrivateIp
	}
	log.Info("prepare to clean up pod [%s] by %s:%s", t.podName, setting.ConnectToAuxproxyByIP, IP)
	// 1. 发送信号
	if err := t.StartCleanUp(IP, log); err != nil {
		log.Error("[delete-pod] sent start clean up pod {%+v} process signal failed:%+v", podInfo, err)
		return err
	}
	// 2. 等待结束
	if err := t.WaitCleanUp(IP, log); err != nil {
		log.Error("[delete-pod] sent start clean up pod {%+v} process signal failed:%+v", podInfo, err)
		return err
	}
	return nil
}

// 发送启动clean up信号
func (t *DelPodTask) StartCleanUp(IP string, log *logger.FMLogger) error {
	url := fmt.Sprintf(startCleanUp, IP)
	for retry := 0; retry < defaultMaxRetry; retry++ {
		code, err := DoCleanup(url)
		if err != nil {
			log.Error("[delete-pod] fail to post cleanup signal to auxproxy %s with error:%+v, retry %d time", IP, err, retry)
			time.Sleep(sleepSecond * time.Second)
			continue
		}
		if code == shutdownCode {
			log.Info("[delete-pod] success post cleanup signal to auxproxy")
			return nil
		} else {
			log.Info("[delete-pod] fail to get shutdown code ,retry %d, resp_code:[%s] ", retry, code)
			time.Sleep(sleepSecond * time.Second)
		}
	}
	return fmt.Errorf("[delete-pod]fail to shutdown auxproxy after %d retry", defaultMaxRetry)
}

// 等待auxproxy返回finish，超时没返回强制删除
func (t *DelPodTask) WaitCleanUp(IP string, log *logger.FMLogger) error {
	url := fmt.Sprintf(shutdownStatePattern, IP)
	for retry := 0; retry < MaxWaitTimes; retry++ {
		buf, err := getCleanUpState(url)
		if err != nil {
			log.Error("[delete-pod]fail to get auxproxy %s state with error:%+v, retry next time", IP, err)
			time.Sleep(sleepSecond * time.Second)
			continue
		}
		if buf == shutdownStateFinished {
			log.Info("[delete-pod]success get auxproxy shutdown state")
			return nil
		} else {
			log.Info("[delete-pod] auxproxy shutdown is not complete ,retry %d, resp:[%s] ", retry, buf)
			time.Sleep(sleepSecond * time.Second)
		}
	}
	log.Error("[delete-pod] failed to get auxproxy state after retry %d times, will force shutdown", MaxWaitTimes)
	return nil
}

func (t *DelPodTask) handlerTaskComplete(log *logger.FMLogger) error {
	if err := db.PodInfoTable().DeletePod(t.podName); err != nil {
		log.Error("[delete-pod] delete vm %s from db error:%+v", t.podName, err)
		return err
	}
	if err := db.DeleteAsyncTask(t.GetType(), t.GetKey()); err != nil {
		log.Error("[delete-pod] delete async task %s error:%+v", t.podName, err)
		return err
	}
	return nil
}
